﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ExpressionMapper
{
    using MappingExpressionGenerator = Func<Expression, Type, Expression, ParameterExpression, Expression>;

    public static partial class Generators
    {
        /// <summary>
        /// Returns generator that uses IConvertible interface for mapping.
        /// </summary>
        public static MappingExpressionGenerator UseIConvertible()
        {
            return
                (fromExpr, to, customMapping, instanceDictionary) => (ImplementsIConvertible(fromExpr.Type) && SupportedByIConvertible(to))
                                                     ? customMapping ?? GenerateIconvertibleExpression(fromExpr, to)
                                                     : null;
        }

        private static Expression GenerateIconvertibleExpression(Expression fromExpr, Type to)
        {
            Debug.WriteLine(string.Format("\tIConvertible ({0}): {1} to {2}", fromExpr.NodeType, fromExpr.Type.Name, to.Name));
            return Expression.Call(fromExpr,
                                   GetIConvertibleMethod(fromExpr.Type, to),
                                   Expression.Constant(null));
        }

        private static MethodInfo GetIConvertibleMethod(Type from, Type to)
        {
            InterfaceMapping map = from.GetInterfaceMap(typeof (IConvertible));
            return map.TargetMethods.First(mi => mi.Name == "To" + to.Name);
        }

        private static bool ImplementsIConvertible(Type t)
        {
            return t.GetInterfaces().Contains(typeof (IConvertible));
        }

        private static bool SupportedByIConvertible(Type t)
        {
            return IsPrimitive(t) && !IsNullable(t);// !t.IsPointer;
        }
    }
}